﻿<html xmlns:MSHelp="http://msdn.microsoft.com/mshelp" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xanx="http://schemas.microsoft.com/developer/xanx/2005"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="save" content="history" /><title>Heightmap Collision Sample</title>
<style><!--
/***********************************************************
 *             SCRIPT-SUPPORTING STYLES
 ***********************************************************/

/* Defines the userData cache persistence mechanism. */
.userDataStyle
{
	behavior: url(#default#userData);
}

/* Used to save the scroll bar position when navigating away from a page. */
div.saveHistory
{
	behavior: url(#default#saveHistory);
}

/* Formats the expand/collapse images for all collapsible regions. */
img.toggle
{
	border: 0;
	margin-right: 5;
}

/* Formats the Language filter drop-down image. */
img#languageFilterImage
{
	border: 0;
	margin-left: 0;
	vertical-align: middle;
}

/* Formats the Members Options filter drop-down image. */
img#membersOptionsFilterImage
{
	border: 0;
	margin-left: 0;
	vertical-align: middle;
}

/* Formats the Collapse All/Expand All images. */
img#toggleAllImage
{
	margin-left: 0;
	vertical-align: middle;
}

/* Supports XLinks */
MSHelp\:link
{
 	text-decoration: underline;
	color: #0000ff; 
	hoverColor: #3366ff;
	filterString: ;
}

body
	{
	background:	#FFFFFF;
	color: #000000;
	font-family:	Verdana;
	font-size: medium;
	font-style: normal;
	font-weight: normal;
	margin-top:	0;
	margin-bottom:	0;
	margin-left:	0;
	margin-right:	0;
	width:	100%;
	/*font-size: 110%;*/
	}

div.section
	{
	margin-left: 15px;
	}

div.hxnx5
	{
	margin-left: 1.5em;
	}

/* Font for all headings */	
h1, h2, h3, h4, h5, h6
	{
	font-family: Verdana, Arial, Helvetica, sans-serif;
	margin-top: 18;
	margin-bottom: 8; 
	font-weight: bold;
	}
h1
	{
	font-size: 130%;
	color: #003399;
	}
div#scrollyes h1 /* Changes font size for full-scrolling topic */
	{
	font-size: 150%;
	}
h2
	{
	font-size: 122%;
	}
h3
	{
	font-size: 115%;
	margin-top: 9;
	margin-bottom: 4; 
	}
h4
	{
	font-size: 115%;
	margin-top: 9;
	margin-bottom: 4; 
	}
h5
	{
	font-size: 100%;
	margin-top: 9;
	margin-bottom: 4; 
	}
h6
	{
	font-size: 100%;
	margin-top: 9;
	margin-bottom: 4; 
	}

ul p, ol p, dl p
	{
	margin-left: 0em;
	}

p
	{
	margin-top: .6em;
	margin-bottom: .6em;
	}
	
td p
	{
	margin-top: 0.0em;
	margin-bottom: 0.6em;
	}

dd p
	{
	margin-top: 0.0em;
	margin-bottom: 0.6em;
	}

.image
	{
	text-align: center;
	}

dl
	{
	margin-top: 0em; 
	margin-bottom: 1.3em;
	}

dd
	{
	margin-bottom: 0em;
	margin-left: 1.5em;
	}

dl.glossary dd 
{
	margin-bottom: 0em;  
	margin-left: 1.5em; 
}

dt
	{
	margin-top: .6em;
	margin-bottom: 1;
	}

ul, ol
	{
	margin-top: 0.6em;
	margin-bottom: 0.6em; 	
	}
	
ol
	{
	margin-left: 2.5em; 	
	}	
	
ul
	{
	list-style-type: disc; 
	margin-left: 1.9em; 
	}

li
	{
	margin-bottom: 0.4em;
	}

ul ol, ol ol
	{
	list-style-type: lower-alpha;
	}

pre
	{
	margin-top: .6em;
	margin-bottom: .6em; 
	font: 105% Lucida, mono; 
	color: #000066;
	}

code
{
	font-family: Monospace, Courier New, Courier;
	font-size: 105%;
	color:	#000066;
}

table.userdata td 
	{
	background: #ffffff;
	background-color: #F5F5F5;
	border-color: #ffffff;
	border: none;
	}	
table.clsWarning
	{
	background: #ffffff;
	padding: 0px;
	margin: 0px;
	border: none;
	}
table.clsWarning td
	{
	padding: 0px;
	margin: 0px;
	background: #ffffff;
	vertical-align: middle;
	font-size: 70%;
	}

div#mainSection table
	{
	width: 95%;
	background: #ffffff;
	margin-top: 5px;
	margin-bottom: 5px;
	}

div#mainSection table th
	{ 
	padding: 5px 6px;
	background: #EFEFF7;
	text-align: left;
	font-size: 70%;
	vertical-align: bottom;
	border-bottom: 1px solid #C8CDDE;
	}
div#mainSection table td
	{ 
	padding: 5px 5px;
	background: #F7F7FF;
	vertical-align: top;
	font-size: 70%;
	border-bottom: 1px solid #D5D5D3;
	}

div#syntaxCodeBlocks table th
	{
	padding: 1px 6px;
	color: #000066;
	}

div#syntaxCodeBlocks table td
	{
	padding: 1px 5px;
	}

/* Applies to the running header text in the first row of the upper table in the
   non-scrolling header region. */
span#runningHeaderText
{
	color: #003399;
	font-size: 90%;
	padding-left: 13;
}

/* Applies to the topic title in the second row of the upper table in the
   non-scrolling header region. */
span#nsrTitle
{
	color: #003399;
	font-size: 120%;
	font-weight: 600;
	padding-left: 13;
}

/* Applies to everything below the non-scrolling header region. */
div#mainSection
{
	font-size: 70%;
	width: 100%;
}

/* Applies to everything below the non-scrolling header region, minus the footer. */
div#mainBody
{
	font-size: 90%;
	margin-left: 15;
	margin-top: 10;
	padding-bottom: 20;
}

/* Adds right padding for all blocks in mainBody */
div#mainBody p, div#mainBody ol, div#mainBody ul, div#mainBody dl
{
	padding-right: 5;
}

div#mainBody div.alert, div#mainBody div.code, div#mainBody div.tableSection
{
	width:98.9%;
}

div.alert p, div.code p
{
	margin-top:5;
	margin-bottom:8;
}

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Begin Note Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
div#mainSection div.alert table
{
	border: 0;
}

div#mainSection div.alert table th
{
	padding-top: 0;
	padding-bottom: 0;
	padding-left: 5;
	padding-right: 5;
}

div#mainSection div.alert table td
{
	padding-left: 5;
	padding-right: 5;
}

img.note
{
	border: 0;
	margin-left: 0;
	margin-right: 3;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - End Note Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Begin Non-scrolling Header Region Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
/* Applies to the entire non-scrolling header region. */
div#header
{
	background-color: #D4DFFF;
	padding-top:	0;
	padding-bottom:	0;
	padding-left:	0;
	padding-right:	0;
	width:	100%;
}

/* Applies to both tables in the non-scrolling header region. */
div#header table
{
	margin-top:	0;
	margin-bottom: 0;
	border-bottom-color: #C8CDDE;
	border-bottom-style: solid;
	border-bottom-width: 1;
	background: #D4DFFF;
	width:	100%;
}

/* Applies to cells in both tables in the non-scrolling header region. */
div#header table td
{
	color: #0000FF;
	font-size: 70%;
	padding-right: 20;
	padding-top: 1;
	padding-bottom: 1;
	border: none;
	background: #D4DFFF;
}

/* Applies to the last row in the upper table of the non-scrolling header region. Text 
   in this row includes See Also, Constructors, Methods, and Properties. */
div#header table tr#headerTableRow3 td
{
	padding-bottom: 2;
	padding-top: 5;
	padding-left: 15;
}

/* Applies to the lower table in the non-scrolling header region. Text in this table
   includes Collapse All/Expand All, Language Filter, and Members Options. */
div#header table#bottomTable
{
	border-top-color: #FFFFFF;
	border-top-style: solid;
	border-top-width: 1;
	text-align: left;
	padding-left: 15;
}


blockquote
	{
	margin-left: 3.8em;
	margin-right: 3.8em;
	margin-top: .6em;
	margin-bottom: .6em;
	}

sup
	{
	text-decoration: none;
	font-size: smaller; 
	}

a:link
	{
	color: #0000FF;
/*    font-weight: bold */
	}
	
a:visited
	{
	color: #0000AA;
/*    font-weight: bold	*/
	}
	
a:hover
	{
	color: #3366FF;
/*    font-weight: bold */
	}
	
.label
	{
	font-weight: bold; 
	margin-top: 1em;
	margin-left: -26px;
	}
	
.tl
	{
	margin-bottom: .75em; 
	}
	
.atl
	{
	padding-left: 1.5em;
	padding-bottom: .75em; 
	}
	
.cfe
	{
	font-weight: bold; 
	}
	
.mini
	{
	font-size: smaller;
	}
	
.dt
	{
	margin-bottom: -.6em; 
	}
	
.indent
	{
	margin-left: 1.9em; 
	margin-right: 1.9em;
	}

.product
	{
	text-align: right;
	color: #333333;
	font-size: smaller;
	font-style: italic;
	}

.buttonbarshade
	{
	position: relative;
	margin: 0;
	left: 0px;
	top: 2;
	width: 50%;
	height: 40px;
	}

.buttonbartable
	{
	position: absolute;
	margin: 0;
	padding:0;
	border:0;
	left:0px;
	top: 2;
	width: 100%;
	height: 40px;
	}

/* background color, font for header */ 
table.buttonbartable td, table.buttonbarshade td
	{
	background: #ffffff; /*#5177B8; #80C615;*/
	border-left: 0px solid #80C615;
	margin: 0;
	padding: 0px 0px 0px 0px;
	font-family: Impact, sans-serif;
	font-size: 14pt;
	}

table.buttonbartable td.button1
	{
	background: #5177B8; /*#80C615;*/;
	padding: 0;
	font-weight: bold;
	text-align: center;
	cursor: hand;
	}

table.buttonbartable td.button2
	{
	background: #5177B8; /*#80C615;*/;
	font-weight: bold;
	text-align: center;
	}

table.buttonbartable td.button3
	{
	background: #5177B8; /*#80C615;*/;
	font-weight: bold;
	text-align: center;
	}

table.buttonbartable td.runninghead
	{
	padding-left: 0px;
	font-style: italic;
	text-align: left;
	}

.version
	{
	text-align: left;
	color: #000000;
	margin-top: 3em;
	margin-left: -26px;
	font-size: smaller;
	font-style: italic;
	}

.lang, .ilang
	{
	color: #0000ff;
	font: normal 7pt Arial, Helvetica, sans-serif;
	}

div.langMenu
	{
	position: absolute;
	z-index: 1;
	width: 96pt;
	padding: 8pt;
	visibility: hidden;
	border: 1px solid #000000;
	background: #ffffd0;
	}

div.langMenu ul
	{
	padding-left: 2em;
	margin-left: 0;
	}

div.filtered
	{
	margin: 4pt 0 8pt -26px;
	padding: 4px 4px 8px 26px;
	width: 100%;
	border: 2px solid #aaaacc;
	background: #ffffff;
	}

div.filtered2
	{
	margin: 4pt 0 8pt -26px;
	padding: 4px 4px 8px 26px;
	width: 100%;
	border: none;
	background: #ffffff;
	}

div.filtered h1, div.filtered h2, div.filtered h3, div.filtered h4
	{
	margin-left: -22px;
	}

div.filtered span.lang
	{
	position: relative;
	left: -22px;
	}

div.reftip
	{
	position: absolute;
	z-index: 1;
	padding: 8pt;
	visibility: hidden;
	border: 1px solid #000000;
	background: #ffffd0;
	}

a.synParam
	{
	color: #0000FF;
	/*color: #3F7800;*/ 	
	/*color: #8DC54F;*/
	text-decoration: none;
    font-weight: normal;
	}

a.synParam:hover
	{
	text-decoration: underline;
    font-weight: normal;
	}

div.sapop
	{
	position: absolute;
	z-index: 1;
	left: 26px;
	width: 100%;
	padding: 10px 10px 10px 36px;
	visibility: hidden;
	border: 1px solid #000000;
	background: #ffffd0;
	}

div.footer
	{
	width: 100%;
	border: none;
	background: #ffffff;
	margin-top: 18pt;
	padding-bottom: 12pt;
	color: #0000FF;
	/*color: #228B22; */
	text-align: center;
	font-size: 76%;
	}

div.preliminary
	{
	margin-top: 8pt;
	padding-bottom: 12pt;
	color: #A0A0A0;
	}

/* A procedure section. eg. 'To create a file', 'To add a value' */
div.proc
    {
	margin-left: 0.5em; 
    }
     
/* The title of a 'procedure' section. */
div.proc h3
    {
	font-family: Verdana, Arial, Helvetica, sans-serif;
	font-weight: bold;
	font-size: 115%;
	margin-top: 1em;
	margin-bottom: 0.4em;
	margin-left: -0.5em; 
	color: #003399;
    }

div.proc ul
    {
    margin-left: 1.5em;
    }

div.proc ol
    {
    margin-left: 2.0em;
    }
      
.note
	{
	margin-left: 14pt;
	margin-right: 12pt;
	}

.indent1
	{
	margin-left: 12pt;
	}

.indent2
	{
	margin-left: 24pt;
	}

.indent3
	{
	margin-left: 36pt;
	}

p.proch
	{
	padding-left: 16px;
	}

p.proch img
	{
	position: relative; 
	vertical-align: top;
	left: -18px; 
	margin-right: -14px; 
	margin-bottom: -18px;
	}
	
div.clsPlatSpec
{
	background-color:#FFF8DC;
	border-style:solid;
	border-width:1pt 0pt 0pt 1pt;
	border-color:#ffE4C4;
	margin-top:0.6em;
	width:100%;
}


/* Applies to the language labels in the Language Filter drop-down list. */
.languageFilter
{
	color:	#0000FF;
	cursor:hand;
	text-decoration:underline;
	padding-bottom:4;
}

/* Dropdown areas */

#languageSpan {
	position: absolute;
	visibility: hidden;
	border-style: solid;
	border-width: 1px;
	border-color: #C8CDDE;
	background: #d4dfff;
	padding: 4px;
	font-size: 70%;
}

#membersOptionsSpan {
	position: absolute;
	visibility: hidden;
	border-style: solid;
	border-width: 1px;
	border-color: #C8CDDE;
	background: #d4dfff;
	padding: 4px;
	font-size: 70%;
}
--></style>

<xml>
<MSHelp:TOCTitle Title="Heightmap Collision Sample" />
<MSHelp:RLTitle Title="Heightmap Collision Sample" />
<MSHelp:Keyword Index="A" Term="O:Microsoft.Xna.HeightmapCollision" />
<MSHelp:Keyword Index="A" Term="5814a24c-91d1-2480-840c-93cd38c34ce4" />
<MSHelp:Keyword Index="K" Term="Heightmap Collision Sample" />

<MSHelp:Attr Name="AssetID" Value="5814a24c-91d1-2480-840c-93cd38c34ce4" />
<MSHelp:Attr Name="Locale" Value="en-us" />
<MSHelp:Attr Name="CommunityContent" Value="1" />
<MSHelp:Attr Name="TopicType" Value="kbOrient" />
</xml>
</head><body><div id="mainSection"><div id="mainBody">

  <h1>Heightmap Collision Sample</h1>

  This sample demonstrates how to move objects along a heightmap. It is based on the 
  Generated Geometry Sample, which creates a landscape from a bitmap. We build upon that sample, showing how to quickly calculate the height of any point on that heightmap.
  <a id="ID2EK" name="ID2EK"> </a><h1 class="heading">Sample Overview</h1><div id="ID2EK" class="hxnx1">
    
    <p>
      The Generated Geometry Sample introduced the concept of a heightmap. In that sample,
      a content processor reads a bitmap and uses the intensity of its pixels as height values on a terrain.
      A logical next step to this technique is to place objects on the terrain. This sample demonstrates
      that technique by placing a ball on the generated terrain.
    </p>

    <a id="ID2EQ" name="ID2EQ"> </a><h2 class="subHeading">Sample Controls</h2><div id="ID2EQ" class="hxnx2">
      
      <p>This sample uses the following keyboard and gamepad controls.</p>

      <table>
        <tr>
          <th>Action</th>
          <th>Keyboard control</th>
          <th>Gamepad control</th>
        </tr>
        <tr>
          <td>Move the ball</td>
          <td>
            <p>UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW or W, A, S, and D</p>
          </td>
          <td>Left thumb stick or D-pad</td>
        </tr>
        <tr>
          <td>Exit the sample</td>
          <td>ESC or ALT+F4</td>
          <td>
            <b>BACK</b>
          </td>
        </tr>
      </table>
    </div>
  </div>

  <a id="ID2E3B" name="ID2E3B"> </a><h1 class="heading">How the Sample Works</h1><div id="ID2E3B" class="hxnx1">
    

    <p>
      This sample is based on the Generated Geometry Sample. To support the added
      functionality, the original sample has been modified in several ways. For clarity, the sky also has been removed.
    </p>
    <a id="ID2ECC" name="ID2ECC"> </a><h2 class="subHeading">Changes to the Terrain Processor</h2><div id="ID2ECC" class="hxnx2">
      
      <p>The <code>TerrainProcessor</code> has been modified slightly for this new sample. First, the algorithm that gives the vertices their XZ position has been modified slightly from the Generated Geometry Sample. In the new version, the positions are calculated so that the heightmap is always centered around the origin. This change simplifies the math at run time.</p>
      <p>In addition, the processor is responsible for creating a class called <code>HeightMapInfoContent</code>, which contains data about the height of the points of the heightmap, as well as the distance between them. This information is attached to the finished terrain model's <code>.Tag</code> property, and will be read when the game loads.</p>
      <p>Finally, the <code>SkyProcessor</code> and <code>SkyContent</code> classes have been removed from the pipeline assembly.</p>
    </div>

    <a id="ID2E1C" name="ID2E1C"> </a><h2 class="subHeading">Rolling Around on the Terrain</h2><div id="ID2E1C" class="hxnx2">
      
      <p>The user is now given direct control over a small sphere, which can roll over the terrain. The camera, which is used to move in a fixed circular pattern around the terrain, is now tied to follow the sphere. To make which code is new more obvious, we removed the <code>Sky</code> class from the sample.</p>
      <p>To keep both the sphere and the camera on top of the terrain, we use the <code>GetHeight</code> function on <code>HeightMapInfo</code>. <code>GetHeight</code> is the most complicated part of this sample. This function accepts a position as an argument and returns the height of the heightmap at that position.</p>
      <img src="zoomedout.png" />
      <p>In this illustration, we are trying to calculate the height of the red circle. The grid represents our heightmap. In this example, our heightmap is 4×4. Note that this means that the heightmap's width and height are only 3 * <code>TerrainScale</code>. We'll call the white space between gridlines "cells."</p>
      <p>To calculate the red circle's height at any point on a cell, we use bilinear interpolation. This is a lot simpler than it sounds. To understand how it works, let's first examine the simpler case, linear interpolation.</p>
      <img src="lerp.png" />
      <p>In this illustration, we know the (x,y) coordinates of two points on the red line: (2,1) and (8,4). We want to find the y-coordinate at x = 4. (The illustration is not to scale, so do not get out your rulers; you will be disappointed.) To find the missing value, we can use linear interpolation. There are several different ways to do this. We'll use a method that, when ported to C# code, lets us make use of <code>MathHelper.Lerp</code> (linear interpolate) and will be fairly efficient.
      </p>
      <p>The process is relatively straightforward. We know the x-coordinates of all three points, and we can see that the x-coordinate of the center point, which we are trying to find, is 1/3 of the way between the x-coordinate of the other two points. (In other words, the distance from x = 2 to x = 4 is one-third of the distance from x = 2 to x = 8.) Since the points are all on a straight line, the y-coordinate must also be 1/3 of the way.</p>
      <p>The distance between the two y-coordinates, y = 1 and y = 4, is 3. So, if the y-coordinate at (4,?) is one-third of that distance away from the point on the left, our missing value is:</p>
      <p>1 + (1/3) × 3 = 2</p>
      <p>Simple enough. Bilinear interpolation will extend that principle further.</p>
      <img src="closeup.png" />
      <p>In this illustration, we are again on a heightmap, trying to calculate the height of our red circle. We know all of the heights at the corners of the cell, since we read those in from the bitmap, remember? We also know the x and z coordinates of the red circle. What we do not know is the height of y.</p>
      <p>First we do a linear interpolation on the top edge of the cell. We go from the (left,top) corner to the (left+1,top) corner, and find the height at point A. Next, we do the same thing on the bottom edge of the cell, calculating the height at point B. Now we know the heights at A and B, both of which are on a straight line with the circle! To find the height of the circle, we do one more linear interpolation between A and B.</p>
      <p>Note that this technique is not perfect: on steep hills, it is still possible that parts of the sphere may clip through the terrain. To avoid this, we would have to perform a much more expensive collision check. Our technique, although imperfect, is inexpensive and yields results that suffice for many games.
      </p>
    </div>
  </div>
  <a id="ID2EWE" name="ID2EWE"> </a><h1 class="heading">Extending the Sample</h1><div id="ID2EWE" class="hxnx1">
    
    <ul>
      <li>The camera in this sample is fairly simple. Try taking the camera from the Chase Camera Sample and putting it into this one.</li>
      <li>When moving objects over a heightmap, you may also want to align them to the slope of the terrain. To do this, you will need to calculate the heightmap's normal vectors in the <code>TerrainProcessor</code>. A <code>GetNormal</code> function would work similarly to <code>GetHeight</code>, except all three components of the vector would have to be interpolated separately, and then renormalized. Using this normal vector, you can calculate an orientation matrix for your object.</li>
    </ul>
  </div>

</div><div class="footer" id="footer"><p>© 2010 Microsoft Corporation. All rights reserved.<br />Send feedback to <a href="mailto:xnags@microsoft.com?subject=Documentation Feedback: Heightmap Collision Sample">xnags@microsoft.com</a>.</p></div></div></body></html>